package com.yeskery.nut.aop;

import com.yeskery.nut.core.ProxyObjectInvokeException;
import com.yeskery.nut.util.ExceptionUtils;
import com.yeskery.nut.util.TypeUtils;

import java.lang.reflect.Method;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 默认的包装的拦截处理器
 * @author sprout
 * @version 1.0
 * 2022-08-28 14:51
 */
public class DefaultWrapInvocationHandler implements WrapInvocationHandler {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(DefaultWrapInvocationHandler.class.getName());

    /** 代理对象上下文 */
    private final ProxyObjectContext proxyObjectContext;

    /** 被代理的对象 */
    private final Object target;

    /**
     * 构建默认的包装的拦截处理器
     * @param proxyObjectContext 代理对象上下文
     * @param target 被代理的对象
     */
    public DefaultWrapInvocationHandler(ProxyObjectContext proxyObjectContext, Object target) {
        if (proxyObjectContext == null) {
            throw new ProxyObjectExecuteException("The ProxyObjectContext Must Not Be Null.");
        }
        this.proxyObjectContext = proxyObjectContext;
        if (target == null) {
            throw new ProxyObjectExecuteException("The Proxy Target Object Must Not Be Null.");
        }
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!proxyObjectContext.shouldProxy(method)) {
            return method.invoke(getTarget(proxy), args);
        }
        List<ProxyMethodHandler> proxyMethodHandlers = proxyObjectContext.getAdapterProxyMethodHandlers(method);
        Execution execution = new DefaultExecution(method, args, target, proxy);
        try {
            // 执行前置处理方法
            for (ProxyMethodHandler handler : proxyMethodHandlers) {
                try {
                    handler.before(method, execution, getTarget(proxy));
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, handler.getClass().getName(), "before", "ProxyMethodHandler["
                            + handler.getClass().getName() + "] Method [before] Execute Fail.", e);
                }
            }

            DefaultInvocation invocation = new DefaultInvocation(method, args, target, proxy, () -> {
                try {
                    return doInvoke(method, getTarget(proxy), args);
                } catch (Exception e) {
                    throw new ProxyObjectInvokeException(e);
                }
            });
            // 执行环绕处理方法
            for (ProxyMethodHandler handler : proxyMethodHandlers) {
                try {
                    handler.around(method, invocation, getTarget(proxy));
                } catch (ProxyAroundAopOriginalInvokeException e) {
                    throw e.getCause();
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, handler.getClass().getName(), "around", "ProxyMethodHandler["
                            + handler.getClass().getName() + "] Method [around] Execute Fail.", e);
                }
            }
            Object result = invocation.getResult();
            // 执行后置处理方法
            for (ProxyMethodHandler handler : proxyMethodHandlers) {
                try {
                    handler.after(method, execution, getTarget(proxy));
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, handler.getClass().getName(), "after", "ProxyMethodHandler["
                            + handler.getClass().getName() + "] Method [after] Execute Fail.", e);
                }
            }
            if (result == null) {
                return null;
            }
            if (method.getReturnType().isAssignableFrom(result.getClass()) || TypeUtils.isAssignable(result.getClass(), method.getReturnType())) {
                return result;
            }
            return null;
        } catch (Throwable throwable) {
            Throwable targetThrowable = ExceptionUtils.getTargetThrowable(throwable);
            targetThrowable = ExceptionUtils.getTargetThrowable(targetThrowable);
            targetThrowable = ExceptionUtils.getTargetThrowable(targetThrowable);
            for (ProxyMethodHandler handler : proxyMethodHandlers) {
                try {
                    handler.throwing(method, execution, getTarget(proxy), targetThrowable);
                } catch (Exception e) {
                    logger.logp(Level.SEVERE, handler.getClass().getName(), "throwing", "ProxyMethodHandler["
                            + handler.getClass().getName() + "] Method [throwing] Execute Fail.", e);
                }
            }
            logger.logp(Level.SEVERE, getTarget(proxy).getClass().getName(), method.getName(), "Proxy Object Execute Fail.", throwable);
            throw targetThrowable;
        }
    }

    /**
     * 执行原对象方法
     * @param method 方法对象
     * @param proxy 代理对象
     * @param args 方法参数数组
     * @return 执行结果
     * @throws Exception 发生的异常
     */
    protected Object doInvoke(Method method, Object proxy, Object[] args) throws Exception {
        return method.invoke(getTarget(proxy), args);
    }

    /**
     * 获取目标对象
     * @param proxy 代理对象
     * @return 目标对象
     */
    protected Object getTarget(Object proxy) {
        return target;
    }
}
